// SPDX-License-Identifier: GPL-2.0
/*
 * Author   : Ryan Shen
 * Contract :
 * File     : hi_drv_wdt.c
 * Describe : hisilicon watchdog driver.
 * Platform : ARM Cortex-A9 & Linux 2.6.34.10
 * Log      : 2013-01-23 : init version completed.
 *
 * Copyright (c) 2010-2012 by Hisilicon. All rights reserved.
 */
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/notifier.h>
#include <asm/io.h>
#include "include/mach/hw_drv_core.h"
#ifdef HW_ONT_CUT_VERSION
#include <linux/kallsyminfo.h>
#endif

extern unsigned long find_trustlist_symbols(const char *name);

// global static variables init to 0 automatically, no need to init
static struct gpio_drv_func g_gpio_drv_func_hook;
static struct i2c_drv_func g_i2c_drv_func_hook;
static struct dev_drv_func g_dev_drv_hook;
static struct pcie_dfx_func g_pcie_dfx_record_hook;
static get_section_cfg_funcptr g_get_section_cfg_hook;
static close_pcie_funcptr g_close_pcie_hook;
static pcie_test_funcptr g_pcie_test_hook;
static feature_issupport_funcptr g_feature_issupport_hook;
static src_radio_skb_handler *g_radio_rx_handler;
static drv_common_file_read_funcptr g_drv_common_file_read_hook;
static drv_common_file_write_funcptr g_drv_common_file_write_hook;
static drv_common_reset_funcptr g_drv_common_reset_hook;
static serdes_lane_init_funcptr g_serdes_lane_init_hook;
static pcie_link_speed_info_get_funcptr g_pcie_link_speed_info_hook;

enum HW_CHIP_ID_E hw_chip_id = HW_CHIP_ID_NONE_E;
EXPORT_SYMBOL(hw_chip_id);

// global variable hooks
struct gpio_drv_func *get_gpio_func_hook(void)
{
	return &g_gpio_drv_func_hook;
}
EXPORT_SYMBOL(get_gpio_func_hook);

struct pcie_dfx_func *get_pcie_dfx_record_func_hook(void)
{
	return &g_pcie_dfx_record_hook;
}
EXPORT_SYMBOL(get_pcie_dfx_record_func_hook);

struct i2c_drv_func *get_i2c_func_hook(void)
{
	return &g_i2c_drv_func_hook;
}
EXPORT_SYMBOL(get_i2c_func_hook);

struct dev_drv_func *get_dev_drv_func(void)
{
	return &g_dev_drv_hook;
}
EXPORT_SYMBOL(get_dev_drv_func);

get_section_cfg_funcptr *get_section_cfg_func_hook(void)
{
	return &g_get_section_cfg_hook;
}
EXPORT_SYMBOL(get_section_cfg_func_hook);

close_pcie_funcptr *get_close_pcie_func_hook(void)
{
	return &g_close_pcie_hook;
}
EXPORT_SYMBOL(get_close_pcie_func_hook);

pcie_test_funcptr *get_pcie_test_func_hook(void)
{
	return &g_pcie_test_hook;
}
EXPORT_SYMBOL(get_pcie_test_func_hook);

feature_issupport_funcptr *get_feature_issupport_func_hook(void)
{
	return &g_feature_issupport_hook;
}
EXPORT_SYMBOL(get_feature_issupport_func_hook);

src_radio_skb_handler **get_radio_rx_handler(void)
{
	return &g_radio_rx_handler;
}
EXPORT_SYMBOL(get_radio_rx_handler);

drv_common_file_read_funcptr *get_drv_common_file_read_func_hook(void)
{
	return &g_drv_common_file_read_hook;
}
EXPORT_SYMBOL(get_drv_common_file_read_func_hook);

drv_common_file_write_funcptr *get_drv_common_file_write_func_hook(void)
{
	return &g_drv_common_file_write_hook;
}
EXPORT_SYMBOL(get_drv_common_file_write_func_hook);

drv_common_reset_funcptr *get_drv_common_reset_func_hook(void)
{
	return &g_drv_common_reset_hook;
}
EXPORT_SYMBOL(get_drv_common_reset_func_hook);

serdes_lane_init_funcptr *get_serdes_lane_init_func_hook(void)
{
	return &g_serdes_lane_init_hook;
}
EXPORT_SYMBOL(get_serdes_lane_init_func_hook);

pcie_link_speed_info_get_funcptr *get_pcie_link_speed_info_func_hook(void)
{
	return &g_pcie_link_speed_info_hook;
}
EXPORT_SYMBOL(get_pcie_link_speed_info_func_hook);

void set_pcie_link_speed_info_func_hook(pcie_link_speed_info_get_funcptr func)
{
	g_pcie_link_speed_info_hook = func;
}
EXPORT_SYMBOL(set_pcie_link_speed_info_func_hook);

#ifdef HW_ONT_CUT_VERSION
static unsigned long kallsyms_lookup_name_from_global(const char *name)
{
	int i = 0;
	unsigned long paddr = 0;

	for (; i < ARRAY_SIZE(g_sym_addr_map); i++) {
		if (strcmp(g_sym_addr_map[i].sym_name, name) == 0) {
			paddr = g_sym_addr_map[i].sym_addr;
			break;
		}
	}

	if (paddr == 0)
		return 0;

#ifdef CONFIG_THUMB2_KERNEL
	if (paddr)
		paddr |= 1;
#endif

	return paddr;
}

static unsigned long kallsyms_lookup_name_for_tiny(const char *name)
{
	static unsigned long (*local_trustlist)(const char *name);
	static void *(*local_symbol)(const char *symbol);

	unsigned long paddr;

	paddr = kallsyms_lookup_name_from_global(name);
	if (paddr != 0)
		return paddr;

	if (local_trustlist == NULL)
		local_trustlist = (unsigned long(*)(const char *))
			kallsyms_lookup_name_from_global("find_trustlist_symbols");

	paddr = local_trustlist(name);
	if (paddr != 0)
		return paddr;

	if (local_symbol == NULL)
		local_symbol = (void *(*)(const char *))
			kallsyms_lookup_name_from_global("__symbol_get");

	return (unsigned long)local_symbol(name);
}
#endif

/* find name in trustlist */
unsigned long hw_kallsyms_lookup_name(const char *name)
{
	unsigned long funcaddr = 0;
#if defined(HW_ONT_CUT_VERSION)
	funcaddr = kallsyms_lookup_name_for_tiny(name);
#elif defined(CONFIG_RTOS_FIND_TRUSTLIST_SYMBOLS)
	funcaddr = find_trustlist_symbols(name);
#endif
	if (funcaddr == 0)
		funcaddr = (unsigned long)__symbol_get(name);

#ifdef CONFIG_THUMB2_KERNEL
	if (funcaddr)
		funcaddr |= 1;
#endif
	return funcaddr;
}
EXPORT_SYMBOL(hw_kallsyms_lookup_name);

static enum HW_CHIP_ID_E get_chip_id(void)
{
	uint32_t *pui_reg;
	uint32_t ui_st_num;
	uint32_t ui_loop;
	uint32_t ui_ret = HW_CHIP_ID_MPW_E;
	uint32_t reg_val;

	struct hw_ker_chipid_info_s st_chipid_info[] = {
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5115S,    HW_CHIP_ID_5115S_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5115H,    HW_CHIP_ID_5115H_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5115T,    HW_CHIP_ID_5115T_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5116S,    HW_CHIP_ID_5116S_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5116H,    HW_CHIP_ID_5116H_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5116T,    HW_CHIP_ID_5116T_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5116TV5,  HW_CHIP_ID_5116TV5_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5116L,    HW_CHIP_ID_5116L_E},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5118,     HW_CHIP_ID_5118_E},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5118V2,   HW_CHIP_ID_5118V2_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5117H,    HW_CHIP_ID_5117H_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5117P,    HW_CHIP_ID_5117P_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5117V,    HW_CHIP_ID_5117V_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5610H,    HW_CHIP_ID_5610H_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5610T,    HW_CHIP_ID_5610T_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5117L,    HW_CHIP_ID_5117L_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5182H,    HW_CHIP_ID_5182H_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5182S,    HW_CHIP_ID_5182S_E},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5117V_V6, HW_CHIP_ID_5117V_E},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5117P_V5, HAL_CHIP_ID_5117PV5},
		{CHIP_ID_ALL_VERSION_MASK,    CHIP_ID_SD5182H_V5, HAL_CHIP_ID_5182HV5},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5182T,    HAL_CHIP_ID_5182T},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5182S_V5, HAL_CHIP_ID_5182SV5},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5182TCS,  HAL_CHIP_ID_5182T},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD1156H,    HAL_CHIP_ID_1156H},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5190,     HAL_CHIP_ID_5190},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD1155H,    HAL_CHIP_ID_1155H},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD1156EH,   HAL_CHIP_ID_1156E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD2952H,    HAL_CHIP_ID_2952H},
	};

	ui_st_num = sizeof(st_chipid_info)/sizeof(struct hw_ker_chipid_info_s);
	pui_reg = (uint32_t *)ioremap(0x10100800, sizeof(uint32_t));

	reg_val = readl(pui_reg);
	for (ui_loop = 0; ui_loop < ui_st_num; ui_loop++) {
		if ((reg_val & st_chipid_info[ui_loop].ui_chip_mask) ==
			st_chipid_info[ui_loop].ui_chip_value) {
			ui_ret = st_chipid_info[ui_loop].em_chip_id;
			break;
		}
	}
	iounmap(pui_reg);

	return ui_ret;
}

enum HW_CHIP_ID_E hw_kernel_get_chip_id(void)
{
	if (hw_chip_id == HW_CHIP_ID_NONE_E)
		hw_chip_id = get_chip_id();

	return hw_chip_id;
}
EXPORT_SYMBOL(hw_kernel_get_chip_id);

bool is_sd5117p_series(void)
{
	return ((hw_chip_id == HW_CHIP_ID_5117P_E) || (hw_chip_id == HAL_CHIP_ID_5117PV5));
}
EXPORT_SYMBOL(is_sd5117p_series);

static int __init hw_kernel_core_init(void)
{
	(void)hw_kernel_get_chip_id();
	return 0;
}
module_init(hw_kernel_core_init);

static void __exit hw_kernel_core_exit(void) {}
module_exit(hw_kernel_core_exit);

MODULE_IMPORT_NS(HW_RTOS_NS);
MODULE_LICENSE("GPL");
